package com.ai.bitcoin.aop;

import com.fasterxml.jackson.databind.ObjectMapper;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;


public class LogAop {

    //允许最大耗时（毫秒）
    private int MAX_APPLY_TIME = 3000;

    /**
     * 方法描述: 日志打印
     *
     * @param pjp
     * @param log
     */
    public final static Object doAround(ProceedingJoinPoint pjp, Logger log) throws Throwable {
        // before
        long startTimeMillis = System.currentTimeMillis();    // 记录方法开始执行的时间
        String requestPath = pjp.getSignature().getDeclaringTypeName() + "." + pjp.getSignature().getName();
        String classType = pjp.getTarget().getClass().getName();
        Class<?> clazz = Class.forName(classType);
        String clazzName = clazz.getName();
        String methodName = pjp.getSignature().getName(); //获取方法名称
        //获取参数名称和值
        Map<String, Object> inputParamMap = getFieldsName(LogAop.class, clazzName, methodName, pjp.getArgs());
        ObjectMapper objectMapper = new ObjectMapper();
        log.info("访问地址:{}", requestPath);
        log.info("请求参数:{}", objectMapper.writeValueAsString(inputParamMap));

        // after
        Map<String, Object> outputParamMap = new HashMap();
        Object result = pjp.proceed();    // 执行完方法的返回值：调用proceed()方法，就会触发切入点方法执行
        outputParamMap.put("result", result);
        long endTimeMillis = System.currentTimeMillis();    // 记录方法执行完成的时间
        String optTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(startTimeMillis);
        log.info("开始时间:" + optTime + ";总耗时：" + (endTimeMillis - startTimeMillis) + "ms ;");
        log.info("响应结果:{}", objectMapper.writeValueAsString(outputParamMap));
        return result;
    }

    private static Map<String, Object> getFieldsName(Class cls, String clazzName, String methodName, Object[] args) throws NotFoundException {
        Map<String, Object> map = new HashMap();
        ClassPool pool = ClassPool.getDefault();
        ClassClassPath classPath = new ClassClassPath(cls);
        pool.insertClassPath(classPath);
        CtClass cc = pool.get(clazzName);
        CtMethod cm = cc.getDeclaredMethod(methodName);
        MethodInfo methodInfo = cm.getMethodInfo();
        CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
        LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
        int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
        for (int i = 0; i < cm.getParameterTypes().length; i++) {
            if (args.length > 0) {
                if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse) {
                    continue;
                }
                if (i < args.length) {
                    map.put(attr.variableName(i + pos), args[i]);//paramNames即参数名
                }
            }
        }
        return map;
    }
}
